package org.jackysoft.cas;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.constraints.NotNull;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.ServiceTicketImpl;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicketImpl;
import org.jasig.cas.ticket.registry.AbstractDistributedTicketRegistry;
import org.springframework.transaction.annotation.Transactional;

public class HibernateTicketRegistry extends AbstractDistributedTicketRegistry {

	@NotNull
	private String ticketGrantingTicketPrefix = "TGT";

	@NotNull
	private SessionFactory sessionFactory;

	@Override
	@Transactional(readOnly = false)
	public void addTicket(Ticket ticket) {
		getSession().persist(ticket);
	}

	@Override
	@Transactional(readOnly = false)
	public boolean deleteTicket(String ticketId) {
		final Ticket ticket = getRawTicket(ticketId);

		if (ticket == null) {
			return false;
		}

		if (ticket instanceof ServiceTicket) {
			removeTicket(ticket);
			return true;
		}

		deleteTicketAndChildren(ticket);
		return true;
	}

	@Override
	public Ticket getTicket(String ticketId) {
		return getProxiedTicketInstance(getRawTicket(ticketId));
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional(readOnly = true)
	public Collection<Ticket> getTickets() {
		final List<TicketGrantingTicketImpl> tgts = getSession().createQuery(
				"select t from TicketGrantingTicketImpl t").list();
		final List<ServiceTicketImpl> sts = getSession().createQuery(
				"select s from ServiceTicketImpl s").list();

		final List<Ticket> tickets = new ArrayList<Ticket>();
		tickets.addAll(tgts);
		tickets.addAll(sts);

		return tickets;
	}

	public void setTicketGrantingTicketPrefix(String ticketGrantingTicketPrefix) {
		this.ticketGrantingTicketPrefix = ticketGrantingTicketPrefix;
	}

	public String getTicketGrantingTicketPrefix() {
		return ticketGrantingTicketPrefix;
	}

	private Ticket getRawTicket(final String ticketId) {
		try {
			if (ticketId.startsWith(this.ticketGrantingTicketPrefix)) {
				return (Ticket) getSession().get(
						TicketGrantingTicketImpl.class, ticketId);
			}

			return (Ticket) getSession().get(ServiceTicketImpl.class, ticketId);
		} catch (final Exception e) {
			log
					.error("Error getting ticket " + ticketId
							+ " from registry.", e);
		}
		return null;
	}

	private void removeTicket(final Ticket ticket) {
		try {
			if (log.isDebugEnabled()) {
				final Date creationDate = new Date(ticket.getCreationTime());
				log.debug("Removing Ticket >" + ticket.getId() + "< created: "
						+ creationDate.toString());
			}
			getSession().delete(ticket);
		} catch (final Exception e) {
			log.error("Error removing " + ticket + " from registry.", e);
		}
	}

	@SuppressWarnings("unchecked")
	private void deleteTicketAndChildren(final Ticket ticket) {
		final Map<String, Object> params = new HashMap<String, Object>();
		params.put("id", ticket.getId());
		String hql = "select t from TicketGrantingTicketImpl t where t.ticketGrantingTicket.id = :id";
		final List<TicketGrantingTicketImpl> ticketGrantingTicketImpls = getSession()
				.createQuery(hql).setString("id", ticket.getId()).list();
		hql = "select s from ServiceTicketImpl s where s.ticketGrantingTicket.id = :id";
		final List<ServiceTicketImpl> serviceTicketImpls = getSession()
				.createQuery(hql).setString("id", ticket.getId()).list();

		for (final ServiceTicketImpl s : serviceTicketImpls) {
			removeTicket(s);
		}

		for (final TicketGrantingTicketImpl t : ticketGrantingTicketImpls) {
			deleteTicketAndChildren(t);
		}

		removeTicket(ticket);
	}

	private Session getSession() {
		Session sess = null;
		try {
			sess = this.sessionFactory.getCurrentSession();
		} catch (HibernateException e) {
			return this.sessionFactory.openSession();
		}
		if (sess == null)
			sess = this.sessionFactory.openSession();
		return sess;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	@Override
	protected boolean needsCallback() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	protected void updateTicket(Ticket ticket) {
		getSession().merge(ticket);
		
		
	}
}
